package org.cmcc.pcap.packet.capture;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.cmcc.pcap.packet.util.PacketAnalysisConfig;
import org.pcap4j.core.BpfProgram;
import org.pcap4j.core.PcapHandle;
import org.pcap4j.core.Pcaps;

import java.io.File;
import java.io.FilenameFilter;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.text.DecimalFormat;

public class JCaptures {
	private static Logger logger = LogManager.getLogger(JCaptures.class);
	
//	public boolean complete = false;//标识单次一个pcap文件处理过程是否全部完成
	
	//标记测试数据包中是否有tcp包或者udp包，true表示有，false表示没有
	public boolean tcp_packet_exist;
	public boolean udp_packet_exist;
	//标记tcp或者udp包是否处理完毕
	public boolean tcp_complete;
	public boolean udp_complete;
	//此处是否要新增两个标记位，一个用来标识tcp，一个用来标识udp
	public boolean fileRealSerial = false ;
	public String filter;
	private PacketHandler packetHandler;
	PcapHandle captor ;
	public Timestamp timestamp;
	private String firstFilter;
  
//20180420
//	private static JCaptures jCaptures;
//	public static JCaptures getInstance(){//懒汉...需要线程安全吗？
//		jCaptures = jCaptures == null ? new JCaptures() : jCaptures;
//		return jCaptures;
//	}	
	

	public JCaptures(String taskId){
		logger.info("< I just tell you , I have started >");
		logger.debug("\n < System Env >  : \n"+System.getenv().toString().replaceAll(",", "\n"));
		this.filter = PacketAnalysisConfig.getConfigAsString("jpcap.filter").trim();
		this.packetHandler = new PacketHandler(this,taskId);
		this.fileRealSerial = "FileRealSerial".equalsIgnoreCase(PacketAnalysisConfig.getConfigAsString("pcap.deal.mode"));
		logger.info("FileRealSerial : ["+fileRealSerial+"]");
		logger.info("Jcap Filter : ["+filter+"]");
		this.firstFilter = PacketAnalysisConfig.getConfigAsString("network.interface.filter").trim();
	}
	
	
	/**
	 * 停止抓取包,结束所有处理线程
	 */
	public void stop(){
		logger.info("[ stoping JCaptures ]");
		if(captor != null)
		    captor.close();
		if(this.packetHandler != null){
			this.packetHandler.stop();
			this.packetHandler = null;
		}
		logger.info("[ stoped JCaptures ]");
	}
	/**
	  *  从单个pcap文件读取抓包. TODO 判断文件后缀类型？
	  *  外部多次调用该方法时，每个文件串行处理
	 * @param filePath
	 */
	public void captureFromFile(String filePath, PacketHandler packetHandler){

		logger.debug("this is udp test !");
		try {
			captor= Pcaps.openOffline(filePath);
			/**
			 * captor.setFilter(arg0, arg1);//TODO 设置过滤器,构建一些常用的过滤规则？
			 * captor.setFilter("tcp", true);
			 * jpcap.setFilter("tcp and src 172.16.101.99", true);
			 * captor.setFilter("vlan", true); 
			 * 如何过滤HTTP？
			 */
			
			logger.debug("filter is :" + filter);

			//过滤条件中如有vlan,vlan一定放在条件的末尾.否则会只判断是不是vlan
			captor.setFilter("ip or tcp ", BpfProgram.BpfCompileMode.NONOPTIMIZE);

			captor.loop(-1, this.packetHandler);//此处循环读文件，读完一个文件方法结束


			while(fileRealSerial) {// 此处决定读入单个文件的所有packet后，是否阻塞主线程，当前文件处理完时，跳出循环。
//				if(complete) {
				if(tcp_complete && udp_complete){
					break;
				}
				Thread.sleep(1000);
			}

		}
//		catch (InterruptedException e) {
//			logger.error("Some InterruptedException",e);
//		} 
		catch (Exception e){
			logger.error("Some Exception",e);
		}
	}
	/**
	  *  从多个pcap文件读取抓包
	 * @param filePaths
	 */
	public void captureFromFiles(String[] filePaths, PacketHandler packetHandler){
		for(String filePath : filePaths){
//			this.complete = false;
			this.tcp_complete = false;
			this.udp_complete = false;
			this.captureFromFile(filePath,packetHandler);
		}
	}
	
	/**
	 * 遍历一个目录
	 */
	public void captureFromDirectory(String dirPath, PacketHandler packetHandler){
		
		FilenameFilter filenameFilter = new FilenameFilter(){
			@Override
			public boolean accept(File file, String name) {//这里的name属性，表示文件或文件夹的名字，
				                                           //可以直接根据名字做前缀、后缀以及正则表达式等进行过滤，
				                                           //如：return name.startsWith("ra");
				return name.matches("(.*\\.pcap)|(.*\\.cap)");
				//return name.contains(".pcap");//性能是否不如正则表达式?
			}
		};
		
		File dir = new File(dirPath);
		if( !dir.isDirectory() ){
			logger.error("the input is not a directory ");
			return;
		}
		
		File[] files = dir.listFiles(filenameFilter);
		
		logger.info("the fileNumber is : "+files.length);
		//logger.info("the dir total length is : "+dir.length());//8192
		
		int totalBytes = 0;
		for(File file : files){
			totalBytes += file.length();
			logger.info("the < "+file.getName()+" > file.lentgh is : "+file.length());
		}
		logger.info("the dir total length is : "+totalBytes);
		
		int dealTotalBytes = 0;
		for(File file : files){
			dealTotalBytes += file.length();
//			this.complete = false;
			
			this.tcp_complete = false;
			this.udp_complete = false;
			
			this.captureFromFile(file.getAbsolutePath(),packetHandler);
			
			DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance();
			decimalFormat.setMaximumFractionDigits(2);
			decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
			float progressNum = (float)dealTotalBytes/(float)totalBytes * 100;
			
			String progressStr = decimalFormat.format(progressNum);
			logger.info("the pcap deal rate of progress is : "+ progressStr +" %");
		}
		
	}
}
